package vrp;

/**
 * Class representing a vehicle (and its salesman ^^)
 * Comparison between two vehicles is made by compraing their first visited customer
 * @author pmartin
 *
 */
public class Vehicle implements Comparable<Vehicle>{
    
    /**
     * Vehicle's name (id)
     */
    private String name;
    
    /**
     * First customer to visit in the round
     */
    private Customer firstCustomer = Constants.depot;
    
    /**
     * Usage of the vehicle. When the vehicle is full, usage equals to
     * Constants.carCapacity
     */
    private int usage = 0;
    
    /**
     * Previous car (round) in the solution containing this vehicle Null if it
     * is the first one.
     */
    private Vehicle prevCar = null;
    
    /**
     * Next car (round) in the solution containing this vehicle Null if it is
     * the last one.
     */
    private Vehicle nextCar = null;
    
    /**
     * Length of the round associated to this vehicle
     */
    private double length = 0.0;
    
    /**
     * Number of customers in the round
     */
    private int numCustomers = 0;
    
    private int id;
    
    private Solution solution;
    
    /**
     * @inheritDoc
     */
    public String toString() {
        return getName();
    }
    
    /**
     * @return Returns the firstCustomer.
     */
    public Customer getFirstCustomer() {
        return firstCustomer;
    }

	/**
     * @return Returns the name.
     */
    public String getName() {
        return name;
    }
    
    /**
     * @param name
     *            The name to set.
     */
    
    public void setName(String name) {
        this.name = name;
    }
    
    int getUsage() {
        return usage;
    }
    
    void setUsage(int usage) {
        this.usage = usage;
    }
    
    void updateUsage(int update) {
        usage += update;
    }
    
    void setPrevCar(Vehicle prevCar) {
        this.prevCar = prevCar;
    }
    
    void setNextCar(Vehicle nextCar) {
        this.nextCar = nextCar;
    }
    
    Vehicle getPrevCar() {
        return prevCar;
    }
    
    Vehicle getNextCar() {
        return nextCar;
    }
    
    void setId(int id) {
        this.id = id;
    }
    
    int getId() {
        return id;
    }
    
    boolean isFirstCar() {
        return (prevCar==null);
    }
    
    void deleteSelf() {
        if (usage!=0) throw new RuntimeException("Incorrect Calculations, as trying to delete a non-empty vehicle");
        if (prevCar!=null) prevCar.setNextCar(nextCar);
        if (nextCar!=null) nextCar.setPrevCar(prevCar);
        if (solution.getFirstVehicle().getId()==id) solution.setFirstVehicle(nextCar);
        solution.decNumVehiclesby1();
        firstCustomer=null;
        nextCar=null;
        prevCar=null;
    }

    /**
     * @param firstCustomer
     *            The firstCustomer to set.
     */
    public void insertAsFirstCustomer(Customer firstCustomer) {
        if(this.firstCustomer != Constants.depot){
            firstCustomer.setNextCustomer(this.firstCustomer);
            this.firstCustomer.setPrevCustomer(firstCustomer);
        } else {
            firstCustomer.setNextCustomer(Constants.depot);
        }
        firstCustomer.setPrevCustomer(Constants.depot);
        firstCustomer.setVehicle(this);
        this.firstCustomer = firstCustomer;
        this.firstCustomer.setPosition(0);
        this.updateUsage(firstCustomer.getReq());
        this.increaseLength(firstCustomer);
        //this.numCustomers++;
    }
    
    /**
     * Replaces the pointer to the first Customer.
     * WARNING : No linking is made ...
     * @param c
     */
    public void setFirstCustomer(Customer c){
    	this.firstCustomer = c;
    }
    
    /**
     * Add a customer at the given position in the round
     * @param customer Customer to add
     * @param index Index where the customer must be added (0 = firstCustomer)
     */
    void addCustomer(Customer customer, int index) {
    	//System.out.println(this.id+" : "+index+" / "+this.numCustomers);
        if (index<0) throw new RuntimeException("wrong index");
        
        if(index == 0){
            this.insertAsFirstCustomer(customer);
        } else {
	        Customer aux=firstCustomer;
	        while(index>1) {
	            aux=aux.getNextCustomer();
	            --index;
	        }
	        // TODO Refactor these lines of code in the same method
	        customer.setPrevCustomer(aux);
	        customer.setNextCustomer(aux.getNextCustomer());
	        aux.setNextCustomer(customer);
	        customer.getNextCustomer().setPrevCustomer(customer);
	        customer.setVehicle(this);
	        customer.setPosition(index);
	        this.updateUsage(customer.getReq());
	        this.increaseLength(customer);
	        //this.numCustomers++;
        }
    }
    
    /**
     * Add a customer at the last position in the round
     * @param customer Customer to add
     */
    public void addLastCustomer(Customer customer) {
        Customer aux = firstCustomer;
        int customerPosition = 1;
        if(aux == Constants.depot){
            // Empty list
            this.insertAsFirstCustomer(customer);
        } else {
            while (aux.getNextCustomer() != Constants.depot) {
                aux = aux.getNextCustomer();
                customerPosition++;
            }
            customer.setNextCustomer(Constants.depot);
            customer.setPrevCustomer(aux);
            aux.setNextCustomer(customer);
            customer.setVehicle(this);
            customer.setPosition(customerPosition);
            this.updateUsage(customer.getReq());
            this.increaseLength(firstCustomer);
            //this.numCustomers++;
        }
    }
    
    /**
     * Simulate adding req objects to a vehicle containing usage objects and give the resulting overflow
     * @param usage Usage of the vehicle to simulate
     * @param req Number of objects we would like to add
     * @return Resulting overflow (0 if there is enough space for the req objects)
     */
    static int overflow(int usage, int req) {
        int cap = Constants.carCapacity;
        if (usage + req <= cap)
            return 0;
        else if (usage - cap >= 0)
            return req;
        else
            return (usage + req - cap);
    }
    /**
     * Simulate adding req objects to the current vehicle and give the resulting overflow
     * @param req Number of objects we would like to add
     * @return Resulting overflow (0 if there is enough space for the req objects)
     */
    public int overflow(int req) {
        return overflow(this.usage, req);
    }
    
    /**
     * Returns the cost decrease if we remove a customer from the current vehicle
     * @param customer Customer we'd like to remove
     * @return Cost inherent to the removal
     *
    double inherentDec(Customer customer) {
        Customer prev=customer.getPrevCustomer();
        Customer next=customer.getNextCustomer();
        double distCh=customer.distanceChange(prev,next);
        return (distCh+Constants.alpha*overflow(usage-customer.getReq(),customer.getReq()));
    }*/
    /**
     * Returns the length decrease if we remove a customer from the current vehicle
     * @param customer Customer we'd like to remove
     * @return Length inherent to the removal
     */
    double inherentDec(Customer customer) {
        Customer prev=customer.getPrevCustomer();
        Customer next=customer.getNextCustomer();
        return customer.distanceChange(prev,next);
    }
    
    /**
     * Method permitting to write the vehicule round to the screen for debugging purposes
     */
    public void printDebug(){
        // Recap line
        System.out.println("\t Vehicle n°"+this.id+" - Usage : "+this.usage+" - Round length : "+this.length+" - "+this.numCustomers+" customers");
        // Start to show the rounds
        System.out.print("\t");
        Customer currCustomer = this.firstCustomer;
        while(currCustomer != Constants.depot){
            System.out.print("\t"+currCustomer.getPosition()+" [Id:"+currCustomer.getId()+/*" / "+currCustomer.getAngle()+" rd "+*/"/ Req: "+currCustomer.getReq()+"]");
            currCustomer = currCustomer.getNextCustomer();
        }
    }
    
    /**
     * Increase the length of the current round by taking in account the given customer to visit 
     * @param c Customer to visit
     */
    private void increaseLength(Customer c) {
		this.length += c.distanceFrom(c.getPrevCustomer());
		this.length += c.distanceFrom(c.getNextCustomer());
	}
    
    /**
     * Decrease the number of vehicles of the current round by taking
     */
    public void decreaseNumCustomers() {
    	this.numCustomers--;
    }
    
    @Override
    public int compareTo(Vehicle o) {
        int resu = 0;
        if(this.getFirstCustomer() == null){resu = 1;} 
        else if(o.getFirstCustomer() == null){resu = -1;} 
        else {
            int selfFirstCustomerId = this.getFirstCustomer().getId();
            int otherFirstCustomerId = o.getFirstCustomer().getId();
            if(selfFirstCustomerId < otherFirstCustomerId){
                resu = -1;
            } else if (selfFirstCustomerId > otherFirstCustomerId){
                resu = 1;
            }
        }
        return resu;
    }

	public double getLength() {
		return length;
	}

	public void setLength(double length) {
		this.length = length;
	}

	public int getNumCustomers() {
		return numCustomers;
	}

	public void setNumCustomers(int numCustomers) {
		this.numCustomers = numCustomers;
	}
}
